package secretmanager

import (
	"context"
	"errors"
	"net/http"
	"net/url"
	"path"
	"strconv"

	"github.com/solsw/errorhelper"
	"github.com/solsw/generichelper"
	"github.com/solsw/httphelper"
)

const (
	baseUrl = "https://secretmanager.api.sbercloud.ru/v1/secrets"
)

type (
	Payload struct {
		Data string `json:"data"`
	}
	Secret struct {
		// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_secret.html#
		CreatedAt   string  `json:"createdAt,omitempty"`
		Description string  `json:"description,omitempty"`
		Id          string  `json:"id,omitempty"`
		KmsKeyId    string  `json:"kmsKeyId,omitempty"`
		Labels      any     `json:"labels,omitempty"`
		Name        string  `json:"name,omitempty"`
		Owner       string  `json:"owner,omitempty"`
		ParentId    string  `json:"parentId,omitempty"`
		Payload     Payload `json:"payload,omitempty"`
	}
	AllSecrets struct {
		Secrets []Secret `json:"secrets"`
	}
)

// GetAllSecrets возвращает список секретов.
func GetAllSecrets(ctx context.Context, accessToken, parentId string, pageLimit, pageOffset int) (*AllSecrets, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_secret.html#get--v1-secrets
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__show-secret.html
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__show-secret.html -> API
	if parentId == "" {
		return nil, errorhelper.CallerError(errors.New("no parentId"))
	}
	q := make(url.Values)
	q.Set("parentId", parentId)
	if pageLimit > 0 {
		q.Set("page.limit", strconv.Itoa(pageLimit))
	}
	if pageOffset > 0 {
		q.Set("page.offset", strconv.Itoa(pageOffset))
	}
	u, _ := url.Parse(baseUrl)
	u.RawQuery = q.Encode()
	rq, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	ss, err := httphelper.ReqJson[AllSecrets, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return ss, nil
}

// GetSecret возвращает информацию об указанном секрете.
func GetSecret(ctx context.Context, accessToken, secretId string) (*Secret, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_secret.html#get--v1-secrets-secretId
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId)
	rq, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	s, err := httphelper.ReqJson[Secret, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return s, nil
}

// CreateSecret создаёт секрет.
func CreateSecret(ctx context.Context, accessToken string, secret *Secret) (*Secret, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_secret.html#post--v1-secrets
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__add-secret.html
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	body, err := httphelper.JsonBody(secret)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq, err := http.NewRequestWithContext(ctx, http.MethodPost, baseUrl, body)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	s, err := httphelper.ReqJson[Secret, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return s, nil
}

// UpdateSecret обновляет метаданные секрета.
func UpdateSecret(ctx context.Context, accessToken, secretId, updateMask string, secret *Secret) (*Secret, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_secret.html#patch--v1-secrets-secret.id
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__update-secret.html
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	if updateMask == "" {
		return nil, errorhelper.CallerError(errors.New("no updateMask"))
	}
	q := make(url.Values)
	q.Set("updateMask", updateMask)
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId)
	u.RawQuery = q.Encode()
	body, err := httphelper.JsonBody(secret)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq, err := http.NewRequestWithContext(ctx, http.MethodPatch, u.String(), body)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	s, err := httphelper.ReqJson[Secret, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return s, nil
}

// DeleteSecret удаляет секрет.
func DeleteSecret(ctx context.Context, accessToken, secretId string) error {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_secret.html#delete--v1-secrets-secretId
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__delete-secret.html
	if accessToken == "" {
		return errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return errorhelper.CallerError(errors.New("no secretId"))
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId)
	rq, err := http.NewRequestWithContext(ctx, http.MethodDelete, u.String(), nil)
	if err != nil {
		return errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	_, err = httphelper.ReqBody[generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return errorhelper.CallerError(err)
	}
	return nil
}
